package com.koron.standard.deviceClasssify.excel;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import com.koron.standard.deviceClasssify.bean.excel.TypeExcel;
import com.koron.standard.deviceClasssify.bean.po.Type;
import com.koron.standard.deviceClasssify.bean.po.TypeParams;
import com.koron.standard.deviceClasssify.service.TypeParamsService;
import com.koron.standard.deviceClasssify.service.TypeService;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author jzp
 * @since 2022/5/11
 */
@Slf4j
public class TypeExcelListener extends AnalysisEventListener<TypeExcel> {
    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    private String preTypeId;
    private Integer preRowIndex;

    /**
     * 缓存的数据
     */
    private final List<Type> typeList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    private List<TypeParams> typeParamsList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    private final TypeService typeService;
    private final TypeParamsService typeParamsService;

    public TypeExcelListener(TypeService typeService, TypeParamsService typeParamsService) {
        this.typeService = typeService;
        this.typeParamsService = typeParamsService;
    }

    @Override
    public void invoke(TypeExcel data, AnalysisContext context) {
        Integer rowIndex = context.readRowHolder().getRowIndex();
        // 如果分类编码和分类名称不为空，则创建一条设备分类数据
        if (StrUtil.isNotEmpty(data.getCode()) && StrUtil.isNotEmpty(data.getName())) {
            Type type = data.buildType();
            typeList.add(type);
            preTypeId = type.getId();
            preRowIndex = rowIndex;
        }
        // 如果参数名称不为空。则创建技术参数数据
        if (StrUtil.isNotEmpty(data.getParamName())) {
            TypeParams typeParams = data.buildParam(preTypeId, rowIndex - preRowIndex);
            typeParamsList.add(typeParams);
            // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
            if (typeParamsList.size() >= BATCH_COUNT) {
                saveParams();
                // 存储完成清理 list
                typeParamsList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        String resultCode = buildParentType();
        if (resultCode != null) {
            log.warn("父类编码 {} 未找到", resultCode);
            return;
        }
        saveType();
        saveParams();
        log.info("所有数据解析完成！");
    }

    private void saveType() {
        log.info("{}条数据，开始存储数据库！", typeList.size());
        typeService.saveBatch(typeList);
        log.info("存储数据库成功！");
    }

    private void saveParams() {
        log.info("{}条数据，开始存储数据库！", typeParamsList.size());
        typeParamsService.saveBatch(typeParamsList);
        log.info("存储数据库成功！");
    }

    /**
     * 给设备分类信息都加上父类参数
     * @return 发生错误时返回code，默认null
     */
    private String buildParentType() {
        if (typeList.size() == 0) {
            return null;
        }
        Map<String, Type> map = typeList.stream().collect(Collectors.toMap(Type::getCode, e -> e));
        for (Type type : typeList) {
            String code = type.getCode();
            // 父类code
            String parentCode = code.substring(0, code.length() - 3);
            // 如果parentCode为空，则为跟节点
            if ("".equals(parentCode)) {
                type.setParentId("0");
                type.setParentName("0");
            } else {
                Type parentType = map.get(parentCode);
                // 如果找不到对应的分类标准，则从数据库里面去查
                if (parentType == null) {
                    parentType = typeService.getByCode(parentCode);
                    map.put(parentCode, parentType);
                }
                if (parentType == null) {
                    return code;
                }
                type.setParentId(parentType.getId());
                type.setParentName(parentType.getName());
            }
        }
        return null;
    }

}
